package com.gin.kotlin.gf2gamecommunity.base.util

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.doublePreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

// Datastore创建
// val Context.settings by preferencesDataStore("settings")

/**
 * 扩展方法，设置值
 * @param T 值类型
 * @receiver DataStore<Preferences>
 * @param preferencesKey Preferences.Key<T>
 * @param value T
 * @return Preferences
 */
suspend fun <T : Any> DataStore<Preferences>.setValue(preferencesKey: Preferences.Key<T>, value: T): Preferences = this.edit { p -> p[preferencesKey] = value }

/**
 * 扩展方法,获取值
 * @param T 值类型
 * @receiver DataStore<Preferences>
 * @param preferencesKey Preferences.Key<T>
 * @param defaultValue 默认值
 * @return Flow<T>
 */
fun <T : Any> DataStore<Preferences>.getValue(preferencesKey: Preferences.Key<T>, defaultValue: T): Flow<T> = this.data.map { p -> p[preferencesKey] ?: defaultValue }

/**
 * Datastore中的属性管理器
 * @param T 属性类型
 * @param datastore DataStore<Preferences>
 * @param preferencesKey Preferences.Key<T>
 * @param defaultValue 默认值
 * @param content 正文, value 为当前值，update 为修改属性值的方法
 */
@Composable
fun <T : Any> StoreValue(
    datastore: DataStore<Preferences>, preferencesKey: Preferences.Key<T>, defaultValue: T, content: @Composable (value: T, update: (T) -> Unit) -> Unit
) {
    var setting by remember { mutableStateOf(defaultValue) }
    // 正文是否渲染 （仅当第一次获取配置之后才渲染)
    var show by remember { mutableStateOf(false) }


    val scope = rememberCoroutineScope()
    // Datastore 中的值变化时，同步到 setting 变量
    LaunchedEffect(Unit) {
        datastore.getValue(preferencesKey, defaultValue).collect {
            setting = it
            show = true
        }
    }
    // 正文内容，传入修改setting的方法
    if (show) content(setting) {
        scope.launch { datastore.setValue(preferencesKey, it) }
    }
}

@Composable
fun <T : Any, E : Any> StoreValue(
    datastore: DataStore<Preferences>,
    preferencesKey: Preferences.Key<T>,
    defaultValue: E,
    encode: (E) -> T,
    decode: (T) -> E,
    content: @Composable (value: E, updateE: (E) -> Unit) -> Unit
) = StoreValue(datastore, preferencesKey, encode(defaultValue)) { t, update -> content(decode(t)) { update(encode(it)) } }

/**
 * 扩展方法，设置值
 * @param T Any
 * @receiver DataStore<Preferences>
 * @param key String
 * @param value T
 * @return Preferences
 */
suspend fun <T : Any> DataStore<Preferences>.setValue(key: String, value: T): Preferences = setValue(obtainPreferencesKey(key, value), value)

/**
 * 扩展方法，获取值
 * @param T Any
 * @receiver DataStore<Preferences>
 * @param key String
 * @param defaultValue T
 * @return Flow<T>
 */
fun <T : Any> DataStore<Preferences>.getValue(key: String, defaultValue: T) = getValue(obtainPreferencesKey(key, defaultValue), defaultValue)


private fun <T : Any> obtainPreferencesKey(key: String, value: T): Preferences.Key<T> {
    @Suppress("UNCHECKED_CAST") return when (value) {
        is String -> stringPreferencesKey(key)
        is Int -> intPreferencesKey(key)
        is Long -> intPreferencesKey(key)
        is Float -> floatPreferencesKey(key)
        is Double -> doublePreferencesKey(key)
        is Boolean -> booleanPreferencesKey(key)
        is Set<*> -> stringSetPreferencesKey(key)
        else -> throw RuntimeException("不支持的类型:" + value.javaClass.name)
    } as Preferences.Key<T>
}